package mat;
import java.awt.geom.Point2D;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.Vector;

import javax.swing.JFrame;

import types.MDoc;
import types.MLabel;
import types.MObject;
import types.MPic;

public class Mat extends JFrame{
	/**
	 * MAT main class
	 */
	private static final long serialVersionUID = 1L;
	public Mat(){}
	public Mat(Vector<MObject> mvec, int length){
		vt = new Vector<MObject>();
		OriginVector = mvec;
		matrix = new int[length][length];
		color = new int[length];
		ObjNum = length;
		time = 0;
		for(int i = 0; i< ObjNum; i++)
		{	
			color[i] = 0;
			for(int k = 0; k < ObjNum; k++)
			{
				matrix[i][k] = 0;
			}			
		}
		//need to pass doc_width and doc_height
		initiateDOCPoints();
	}
	
	/*
	 * whenever a object is created, they are added to mat by this method
	 */
	public static void addNode(MObject ob){
		OriginVector.add(ob);
	//	System.out.println("OriginVector's size:"+OriginVector.size());
	}
	
	public static void delNode(int id){
		MObject mo = new MObject();
		for( Iterator<MObject> iter = vt.iterator(); iter.hasNext(); mo = iter.next()){
			if( mo.getId() == id)
				vt.remove(mo);
		}
	}
	/*
	 * topological sorting
	 */
	public static void process(){
		boolean Is_acyclic;
		initiateMAT();
		initialMatrix();
		vt.clear();  //clear all elements in vt first.
		Is_acyclic = DFS();
		if(!Is_acyclic)
		{
			System.out.println("find cycle, error");
		}
	//	System.out.println("vt size:"+vt.size());
	}
	
	/*
	 * initiate the matrix representing the relationship between objects 
	 */
	public static void initialMatrix(){
		for(int i = 0; i < ObjNum; i++)
		{
			MObject tmp1 = new MObject();
			tmp1 = OriginVector.get(i);
			for(int j = 0; j< ObjNum; j++)
			{
				MObject tmp2 = new MObject();
				tmp2 = OriginVector.get(j);	
				if(tmp1.getPosition_to() != null && tmp1.getPosition_to().equals(tmp2))
				{
					matrix[i][j] = 1;
				}
				else
				{
					matrix[i][j] = 0;
				} 
	//			System.out.printf(" %d %d : %d\n", i, j, matrix[i][j]);
			}
		}

	}
	
	public static boolean DFS()
	{
		boolean Is_acyclic = true;
		for(int i = 0; i < ObjNum; i++)
		{ 
			color[i] = WHITE;
		}
		time = 0;
		for(int i = 0; i < ObjNum; i++)
		{
			if(color[i]==WHITE)
			{
				Is_acyclic = DFSVISIT(i);
				if(!Is_acyclic)
				{
					return false;
				}
			}
		}
		return true;
	}
	
	public static boolean DFSVISIT(int n){
		boolean Is_acyclic = true;
		color[n] = GRAY;
		time = time + 1;
		for(int i = 0; i < ObjNum; i++)
		{
			if(matrix[n][i] != 0)
			{
				if(color[i] == WHITE)
				{
					Is_acyclic = DFSVISIT(i);
					if(!Is_acyclic)
					{return false;}
				}
				else if(color[i] == GRAY)
				{
					return false;        // find cycle
				}
			}
		}
		color[n] = BLACK;
		time = time + 1;
		vt.add(OriginVector.get(n));
//		System.out.println("vt size:"+vt.size());
		return true;
	}
	
	public static void printvt(){ // for test
		for(int i = 0; i < vt.size(); i++)
		{
			System.out.println(vt.get(i).getId());
		}
	}
	
	/*
	 * initiate MDoc
	 */
	public static void initMDoc(MDoc md){
		MyDoc = md;
		initiateDOCPoints();
	}
	
	/*
	 * initiate special points in the document
	 */
	public static void initiateDOCPoints()
	{
		lefttop_vertex = new Point2D.Double(0, 0);
		left_vertex = new Point2D.Double(0,doc_height/3);
		leftdown_vertex = new Point2D.Double(0, doc_height*2/3);
		top_vertex = new Point2D.Double(doc_width/3, 0);
		middle_vertex = new Point2D.Double(doc_width/3, doc_height/3);
		down_vertex = new Point2D.Double(doc_width/3, doc_height*2/3);
		righttop_vertex = new Point2D.Double(doc_width*2/3, 0);
		right_vertex = new Point2D.Double(doc_width*2/3, doc_height/3);
		rightdown_vertex = new Point2D.Double(doc_width*2/3, doc_height*2/3);
	}
	
	/*
	 * calculate absolute position of each object
	 */
	public static void calcCoordinate() throws IOException
	{
		MObject tmp;
		MObject tmp_refer;
		//the coordinates of referring object
		double refer_x = 0;
		double refer_y = 0;
		double refer_midx = 0;
		double refer_midy = 0;
		double refer_width = 0;
		double refer_height = 0;		
		double direction = 0;
		double distance = 0;
		
		//result of object coordinate
		double x = 0;
		double y = 0;
		double mid_x = 0;
		double mid_y = 0;
		
		for(int i = 0; i < vt.size(); i++)
		{
			tmp = vt.get(i);
			
			if(tmp.getType() ==  types.MType.PICTYPE){// if it is a picture, load it in. 
				System.out.println(((MPic)tmp).path);
				if(!((MPic)tmp).path.equals("")){
					((MPic)tmp).setImage();
					System.out.println("Successfully load a picture");
				}
					
			}
			
			//if the referred object is DOCTYPE
			if(tmp.getPosition_to()!= null && tmp.getPosition_to().getType()== DOCTYPE) 
			{

				if(tmp.getDirection()==  types.MDirection.INNERMIDDLE)
				{					
					x = (middle_vertex.x + right_vertex.x)/2 - tmp.getWidth()/2;
					y = (middle_vertex.y + down_vertex.y)/2 - tmp.getHeight()/2;
					tmp.setXaxis(x);
					tmp.setYaxis(y);
				}
				else if(tmp.getDirection() ==  types.MDirection.INNERTOP)
				{
					x = (top_vertex.x + righttop_vertex.x)/2 - tmp.getWidth()/2;
					y = (top_vertex.y + middle_vertex.y)/2 - tmp.getHeight()/2;
					tmp.setXaxis(x);
					tmp.setYaxis(y);
				}
				else if(tmp.getDirection() ==  types.MDirection.INNERLEFT)
				{
					x = (left_vertex.x + middle_vertex.x)/2 - tmp.getWidth()/2;
					y = (left_vertex.y + leftdown_vertex.y)/2 - tmp.getHeight()/2;
					tmp.setXaxis(x);
					tmp.setYaxis(y);
				}
				else if(tmp.getDirection() ==  types.MDirection.INNERRIGHT)
				{
					x = (right_vertex.x + doc_width)/2 - tmp.getWidth()/2;
					y = (right_vertex.y + rightdown_vertex.y)/2 - tmp.getHeight()/2;
					tmp.setXaxis(x);
					tmp.setYaxis(y);
				}
				else if(tmp.getDirection() ==  types.MDirection.INNERDOWN)
				{
					x = (down_vertex.x + rightdown_vertex.x)/2 - tmp.getWidth()/2;
					y = (down_vertex.y + doc_height)/2 - tmp.getHeight()/2;
					tmp.setXaxis(x);
					tmp.setYaxis(y);
				}
				else if(tmp.getDirection() ==  types.MDirection.INNERTOPLEFT)
				{
					x = (lefttop_vertex.x + top_vertex.x)/2 - tmp.getWidth()/2;
					y = (lefttop_vertex.y + left_vertex.y)/2 - tmp.getHeight()/2;
					tmp.setXaxis(x);
					tmp.setYaxis(y);
				}
				else if(tmp.getDirection() ==  types.MDirection.INNERTOPRIGHT)
				{
					x = (righttop_vertex.x + doc_width)/2 - tmp.getWidth()/2;
					y = (righttop_vertex.y + right_vertex.y)/2 - tmp.getHeight()/2;
					tmp.setXaxis(x);
					tmp.setYaxis(y);
				}
				else if(tmp.getDirection() ==  types.MDirection.INNERDOWNLEFT)
				{
					x = (leftdown_vertex.x + down_vertex.x)/2 - tmp.getWidth()/2;
					y = (leftdown_vertex.y + doc_height)/2 - tmp.getHeight()/2;
					tmp.setXaxis(x);
					tmp.setYaxis(y);
				}
				else if(tmp.getDirection() ==  types.MDirection.INNERDOWNRIGHT)
				{
					x = (rightdown_vertex.x + doc_width)/2 - tmp.getWidth()/2;
					y = (rightdown_vertex.y + doc_height)/2 - tmp.getHeight()/2;
					tmp.setXaxis(x);
					tmp.setYaxis(y);
				}
			}
			else if(tmp.getPosition_to()!= null && (tmp.getPosition_to().getType()== LABTYPE || tmp.getPosition_to().getType()== CONTYPE ||tmp.getPosition_to().getType()== PICTYPE))
			{
				direction = tmp.getDirection();
				distance =tmp.getDistance();
				if(distance == 0){
					distance = MyDoc.defaultdistance; //set to default distance, if distance is not specified.
					System.out.println(distance);
				}
				//find the referring object
				for(int j = 0; j< vt.size(); j++)
				{
					tmp_refer = vt.get(j);
					if(tmp.getPosition_to().equals(tmp_refer))
					{
						refer_x = tmp_refer.getXaxis();
						refer_y = tmp_refer.getYaxis();
						refer_width = tmp_refer.getWidth();
						refer_height = tmp_refer.getHeight();
						break;
					}
				}
				
				refer_midx = refer_x + refer_width/2;
				refer_midy = refer_y + refer_height/2;
				
				//compute coordinates
				mid_x = refer_midx + distance*Math.cos(Math.toRadians(direction));
				mid_y = refer_midy - distance*Math.sin(Math.toRadians(direction));
				x = mid_x - tmp.getWidth()/2;
				y = mid_y - tmp.getHeight()/2;
				tmp.setXaxis(x);
				tmp.setYaxis(y);
			}
			else{}
		}
	}
	
	
	public static void layout1(){
		process();
		try {
			calcCoordinate();
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		
		MObject mo;
		MCanvas mc = new MCanvas();
		//MCanvas mc = new MCanvas(new ImageIcon("images/background.png").getImage());
		mc.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		System.out.println("vt size:"+vt.size());
		
		for(int i=0; i<vt.size(); i++) {	        
			try {			
				mo = vt.get(i);
				MObject aa = new MLabel();
				MLabel ml;
				aa.getClass();
				String[] bb;
				
				if(aa instanceof MLabel)
					ml = (MLabel)aa;
				else
					;
            System.out.println(i);
			if( mo.getType() == PICTYPE){
				mc.addPic((MPic)mo);
				System.out.println(mo.getId()+""+i+"add a pic into panel");
				
			}else if( mo.getType() == LABTYPE){
				mc.addLabel((MLabel)mo);
				System.out.println(mo.getId()+""+i+"add a label into panel");
				
			}else if( mo.getType() == CONTYPE){

				
			}else if( mo.getType() == DOCTYPE){
				
				
			}else{
				System.out.println("Something Wrong: Object Type Error. "+ mo.getType()+""+mo.getId());
			}
			} catch (ConcurrentModificationException e){
				//e.printStackTrace();
			}
		}
		
		mc.showResult();	
	}
	
	
	public static void poly(MObject[] elemObj, int number, MObject centerObj, int radius,  int degree){
		  //process();
		  //calcCoordinate();
		  
		  //double centerObjX = vt.get(vt.indexOf(centerObj)).getXaxis();
		  //double centerObjY = vt.get(vt.indexOf(centerObj)).getYaxis();
		  System.out.println("poly" + vt.indexOf(centerObj) + "  "+ vt.indexOf(centerObj));
		  if(elemObj.length != number) { 
			  System.out.println("elember # mismatch!");
			  return;
		  }
		  int avgdegree = 360 / elemObj.length;
		  for(int i=0; i<elemObj.length; i++){
			  /*double elemObjX = centerObjX + radius*Math.cos(Math.toRadians(radius)+degree*i);
			  double elemObjY = centerObjY - radius*Math.sin(Math.toRadians(radius)+degree*i);
			  elemObjX = elemObjX + centerObj.getWidth()/2;
			  elemObjY = elemObjY + centerObj.getHeight()/2;
			  elemObj[i].setXaxis(elemObjX);
			  elemObj[i].setYaxis(elemObjY);
			  */
			  elemObj[i].position_to = centerObj;
			  elemObj[i].distance = radius;
			  elemObj[i].direction = degree + avgdegree*i;
		  }
	}

	/*
	 * read text from txt and store them as a String
	 */
	public static String text(String path){
		String tmp_content = "";
		String content = "";
		BufferedReader in = null;
		try {
			in = new BufferedReader(new FileReader(path));
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		try {
			while((tmp_content = in.readLine())!= null){
				content = content + tmp_content+"\n";
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		try {
			in.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
//		System.out.println(content);
		return content;
	}
	
	
	      
		  public static int from(int c, int degree){
			  
			  return (c+degree)%360;
		  }
		  
		  /*
		   * return the degree between two objects
		   */
		  public static int degree(MObject a, MObject b){
			  process();
			  try {
				calcCoordinate();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} // calculate their absulate coordinates on the screen
			  double YEdge = b.getYaxis() - a.getYaxis();
			  double XEdge = b.getXaxis() - a.getXaxis();
			  double TanValue = YEdge/XEdge;
			  double tmp_radian = Math.atan(TanValue);
			  double tmp_degree = Math.toDegrees(tmp_radian);
			  if(YEdge > 0 && XEdge > 0){
				  return (int)(360 - Math.round(tmp_degree));
			  }
			  else if(YEdge > 0 && XEdge < 0){
				  return (int)(180 - Math.round(tmp_degree));
			  }
			  else if(YEdge < 0 && XEdge < 0){
				  return (int)(180 - Math.round(tmp_degree));
			  }
			  else if(YEdge < 0 && XEdge > 0){
				  return (int)(0-Math.round(tmp_degree));
			  }
			  else if(YEdge == 0 && XEdge > 0){
				  return 0;
			  }
			  else if(YEdge == 0 && XEdge < 0){
				  return 180;
			  }
			  else if(XEdge == 0 && YEdge > 0){
				  return 270;
			  }
			  else if(XEdge ==0 && YEdge < 0){
				  return 90;
			  }
			  else return 0;
		  }
		  
		  
		  /*
		   * return the distance between two object
		   */
				  
		  public static int distance(MObject a, MObject b){
	//  initialMatrix();
			  process();
			  try {
				calcCoordinate();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} // calculate their absolute coordinates on the screen
			  double YEdge = b.getYaxis() - a.getYaxis();
			  double XEdge = b.getXaxis() - a.getXaxis();
			  double SqrDistance = YEdge*YEdge + XEdge*XEdge;
			  int Distance = (int) Math.round(Math.sqrt(SqrDistance));
			  return Distance;
		  }
		  
		  public static void printObjects(){
			  initialMatrix();
			  MObject tmp_mo;
			  System.out.println("End Of File"+vt.size());
			  for(int i = 0; i < vt.size(); i++){
				  tmp_mo = vt.get(i);
				  if(tmp_mo.getPosition_to()!= null){
					  System.out.println(tmp_mo.getId()+" position to "+tmp_mo.getPosition_to().getId());
					  System.out.println("X-axis:"+tmp_mo.getXaxis()+" Y-axis:"+tmp_mo.getYaxis()+"");
					  System.out.println("");
				  }
				  else{
					  System.out.println(tmp_mo.getId()+" position to null WARNING");
					  System.out.println("X-axis:"+tmp_mo.getXaxis()+" Y-axis:"+tmp_mo.getYaxis()+"");
					  System.out.println("");
				  }
			  }
		  }
		  
		  public static void initiateMAT(){
			  ObjNum = OriginVector.size();
			  matrix = new int[ObjNum][ObjNum];
			  color = new int[ObjNum];
		  }
		  
		  
	public static int get_DocHeight(){
		return (int) doc_height;
	}
	
	public static int get_DocWidth(){
		return (int) doc_width;
	}
	
	private static Vector<MObject> vt = new Vector();
	private static Vector<MObject> OriginVector = new Vector();
	public static MDoc MyDoc; //doc default setting
	
	private static int[][] matrix;
	private static int ObjNum;
	private static int[] color; //used for DFS
	private static int time; //used for DFS to count finishing time
	private static final int WHITE = 1;
	private static final int GRAY = 2;
	private static final int BLACK = 3;
	
	
	//special positions in document(), for positioning objects that refer to document.
	//l means left; t means top; d means down.
	private static double doc_height = MCanvas.PhoneScreenHeight;
	private static double doc_width = MCanvas.PhoneScreenWidth;
	private static Point2D.Double lefttop_vertex;
	private static Point2D.Double left_vertex;
	private static Point2D.Double leftdown_vertex;
	private static Point2D.Double top_vertex;
	private static Point2D.Double middle_vertex;
	private static Point2D.Double down_vertex;
	private static Point2D.Double righttop_vertex;
	private static Point2D.Double right_vertex;
	private static Point2D.Double rightdown_vertex;
		
	
	//define the object constants
	
	//define the OBJECTTYPE
	private static final int OBJTYPE = 0;
	private static final int PICTYPE = 1;
	private static final int LABTYPE = 2;
	private static final int DOCTYPE = 3;
	private static final int CONTYPE = 4;
}
